package bjtu.tjj.simpledy.service.impl;

import bjtu.tjj.simpledy.common.ResultCode;
import bjtu.tjj.simpledy.dto.PageDTO;
import bjtu.tjj.simpledy.dto.PublishVideoRespDTO;
import bjtu.tjj.simpledy.dto.VideoDTO;
import bjtu.tjj.simpledy.exception.CommonException;
import bjtu.tjj.simpledy.mapper.LikeMapper;
import bjtu.tjj.simpledy.mapper.VideoDTOMapper;
import bjtu.tjj.simpledy.mapper.VideoMapper;
import bjtu.tjj.simpledy.po.VideoPO;
import bjtu.tjj.simpledy.service.VideoService;
import bjtu.tjj.simpledy.utils.FileUtils;
import bjtu.tjj.simpledy.utils.ThreadLocalUtils;
import bjtu.tjj.simpledy.utils.UUIDUtils;
import lombok.RequiredArgsConstructor;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;

@Service
@RequiredArgsConstructor
public class VideoServiceImpl implements VideoService {
    private final static int LABEL_MAX_LEN = 512;
    private final static String COVER_SUFFIX = "jpg";
    private final VideoDTOMapper videoDTOMapper;
    private final VideoMapper videoMapper;
    private final LikeMapper likeMapper;

    @Override
    public PublishVideoRespDTO publish(MultipartFile video, String label) {
        // 检查label
        if (label != null && label.getBytes().length > LABEL_MAX_LEN) {
            throw new CommonException(ResultCode.LABEL_TOO_LONG);
        }
        // 检查文件后缀
        String suffix;
        if ((suffix = FileUtils.getSuffix(video)) == null || !FileUtils.isVideo(suffix)) {
            throw new CommonException(ResultCode.ILLEGAL_VIDEO_SUFFIX);
        }

        // 保存视频
        File videoFile;
        try {
            videoFile = FileUtils.saveFile(video.getBytes(), suffix);
        } catch (IOException e) {
            throw new CommonException(ResultCode.SAVE_FAILED);
        }
        // 抽取第一帧
        File coverFile;
        long length;
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoFile)) {
            grabber.start();
            Frame frame = grabber.grabImage();
            if (frame == null) {
                throw new CommonException(ResultCode.SAVE_FAILED);
            }

            Java2DFrameConverter converter = new Java2DFrameConverter();
            BufferedImage buffer = converter.getBufferedImage(frame);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

            ImageIO.write(buffer, COVER_SUFFIX, outputStream);
            coverFile = FileUtils.saveFile(outputStream.toByteArray(), COVER_SUFFIX);
            converter.close();
            long microseconds = grabber.getLengthInTime();
            length = microseconds / 1000;
            grabber.stop();
        } catch (IOException e) {
            throw new CommonException(ResultCode.SAVE_FAILED);
        }
        // 插入到数据库
        VideoPO videoPO = new VideoPO();
        videoPO.setId(UUIDUtils.next());
        videoPO.setFilename(videoFile.getName());
        videoPO.setCover(coverFile.getName());
        videoPO.setUserId(ThreadLocalUtils.getUserId());
        videoPO.setLabel(label);
        videoPO.setLength(length);
        int res = videoMapper.insert(videoPO);
        if (res == 0) {
            throw new CommonException(ResultCode.SAVE_FAILED);
        }

        PublishVideoRespDTO dto = new PublishVideoRespDTO();
        BeanUtils.copyProperties(videoPO, dto);
        dto.setVideoId(videoPO.getId());
        return dto;
    }

    @Override
    public void delete(Long videoId) {
        int res = videoMapper.delete(videoId);
        if (res == 0) {
            throw new CommonException(ResultCode.VIDEO_NOT_EXIST);
        }
        likeMapper.deleteVideo(videoId);
    }

    @Override
    public List<VideoDTO> getLiked(Integer page, Integer size) {
        return videoDTOMapper.getLiked(ThreadLocalUtils.getUserId(), new PageDTO(page, size));
    }

    @Override
    public List<VideoDTO> getPublished(Integer page, Integer size) {
        return videoDTOMapper.getPublished(ThreadLocalUtils.getUserId(), new PageDTO(page, size));
    }
}
